home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
magazine
/
drdobbs
/
1990
/
04
/
dunteman.lst
< prev
next >
Wrap
File List
|
1990-03-09
|
11KB
|
340 lines
STRUCTURED PROGRAMMING COLUMN
by Jeff Duntemann
[LISTING ONE]
{---------------------------------------------------}
{ TIMEDATE }
{ }
{ A Time-and-date stamp object for Turbo Pascal 5.5 }
{ }
{ by Jeff Duntemann }
{ Last update 12/23/89 }
{ }
{ NOTE: This unit should be good until December 31, }
{ 2043, when the long integer time/date stamp turns }
{ negative. HOWEVER, the Zeller's Congruence }
{ algorithm shown here fails at the end of the 20th }
{ century. I should be able to figure out the fix }
{ by then... }
{---------------------------------------------------}
UNIT TimeDate;
INTERFACE
USES DOS;
TYPE
String9 = STRING[9];
String20 = STRING[20];
String50 = STRING[50];
When =
OBJECT
WhenStamp : LongInt; { Combined time/date stamp }
TimeString : String9; { i.e., "12:45a" }
Hours,Minutes,Seconds : Word; { Seconds is always even! }
DateString : String20; { i.e., "06/29/89" }
LongDateString : String50; { i.e., "Thursday, June 29, 1989" }
Year,Month,Day : Word;
DayOfWeek : Integer; { 0=Sunday, 1=Monday, etc. }
FUNCTION GetTimeStamp : Word; { Returns DOS-format time stamp }
FUNCTION GetDateStamp : Word; { Returns DOS-format date dtamp }
PROCEDURE PutNow;
PROCEDURE PutWhenStamp(NewWhen : LongInt);
PROCEDURE PutTimeStamp(NewStamp : Word);
PROCEDURE PutDateStamp(NewStamp : Word);
PROCEDURE PutNewDate(NewYear,NewMonth,NewDay : Word);
PROCEDURE PutNewTime(NewHours,NewMinutes,NewSeconds : Word);
END;
IMPLEMENTATION
{ Keep in mind that all this stuff is PRIVATE to the unit! }
CONST
MonthTags : ARRAY [1..12] of String9 =
('January','February','March','April','May','June','July',
'August','September','October','November','December');
DayTags : ARRAY [0..6] OF String9 =
('Sunday','Monday','Tuesday','Wednesday',
'Thursday','Friday','Saturday');
TYPE
WhenUnion =
RECORD
TimePart : Word;
DatePart : Word;
END;
VAR
Temp1 : String50;
Dummy : Word;
{ Some utility routines private to this unit: }
FUNCTION CalcTimeStamp(Hours,Minutes,Seconds : Word) : Word;
BEGIN
CalcTimeStamp := (Hours SHL 11) OR (Minutes SHL 5) OR (Seconds SHR 1);
END;
FUNCTION CalcDateStamp(Year,Month,Day : Word) : Word;
BEGIN
CalcDateStamp := ((Year - 1980) SHL 9) OR (Month SHL 5) OR Day;
END;
PROCEDURE CalcTimeString(VAR TimeString : String9;
Hours,Minutes,Seconds : Word);
VAR
Temp1,Temp2 : String9;
AMPM : Char;
I : Integer;
BEGIN
I := Hours;
IF Hours = 0 THEN I := 12; { "0" hours = 12am }
IF Hours > 12 THEN I := Hours - 12;
IF Hours > 11 THEN AMPM := 'p' ELSE AMPM := 'a';
Str(I:2,Temp1); Str(Minutes,Temp2);
IF Length(Temp2) < 2 THEN Temp2 := '0' + Temp2;
TimeString := Temp1 + ':' + Temp2 + AMPM;
END;
PROCEDURE CalcDateString(VAR DateString : String20;
Year,Month,Day : Word);
BEGIN
Str(Month,DateString);
Str(Day,Temp1);
DateString := DateString + '/' + Temp1;
Str(Year,Temp1);
DateString := DateString + '/' + Copy(Temp1,3,2);
END;
PROCEDURE CalcLongDateString(VAR LongdateString : String50;
Year,Month,Date,DayOfWeek : Word);
VAR
Temp1 : String9;
BEGIN
LongDateString := DayTags[DayOfWeek] + ', ';
Str(Date,Temp1);
LongDateString := LongDateString +
MonthTags[Month] + ' ' + Temp1 + ', ';
Str(Year,Temp1);
LongDateString := LongDateString + Temp1;
END;
{---------------------------------------------------------------------}
{ This calculates a day of the week figure, where 0=Sunday, 1=Monday, }
{ and so on, given the year, month, and day. The year may be passed }
{ as either "1989" or "89" but *not* as 1980-relative, or "9". Also }
{ note that this particular algorithm turns into a pumpkin in 2000. }
{ BTW, don't ask me to explain how this crazy thing works. I haven't }
{ the foggiest notion. If I ever meet Mr. Zeller, I'll ask him. }
{---------------------------------------------------------------------}
FUNCTION CalcDayOfWeek(Year,Month,Day : Word) : Integer;
VAR
Century,Leftovers,Holder : Integer;
BEGIN
{ First test for error conditions on input values: }
IF (Year < 0) OR
(Month < 1) OR (Month > 12) OR
(Day < 1) OR (Day > 31) THEN
CalcDayOfWeek := -1 { Return -1 to indicate an error }
ELSE
{ Do the Zeller's Congruence calculation: }
BEGIN
IF Year < 100 THEN Inc(Year,1900);
Dec(Month,2);
IF (Month < 1) OR (Month > 10) THEN
BEGIN
Dec(Year,1);
Inc(Month,12);
END;
Century := Year DIV 100;
Leftovers := Year MOD 100;
Holder := (Trunc(Int(2.6 * Month - 0.2)) + Day +
Leftovers + (Leftovers DIV 4) +
(Century DIV 4) - Century - Century) MOD 7;
IF Holder < 0 THEN
Inc(Holder,7);
CalcDayOfWeek := Holder;
END;
END;
{***************************************}
{ Method implementations for type When: }
{***************************************}
{---------------------------------------------------------------------}
{ There will be many times when an individual date or time stamp will }
{ be much more useful than a combined time/date stamp. These simple }
{ functions return the appropriate half of the combined long integer }
{ time/date stamp without incurring any calculation overhead. It's }
{ done with a simple value typecast: }
{---------------------------------------------------------------------}
FUNCTION When.GetTimeStamp : Word;
BEGIN
GetTimeStamp := WhenUnion(WhenStamp).TimePart;
END;
FUNCTION When.GetDateStamp : Word;
BEGIN
GetDateStamp := WhenUnion(WhenStamp).DatePart;
END;
{---------------------------------------------------------------------}
{ To fill a When record with the current time and date as maintained }
{ by the system clock, execute this method: }
{---------------------------------------------------------------------}
PROCEDURE When.PutNow;
BEGIN
{ Get current clock time. Note that we ignore hundredths figure: }
GetTime(Hours,Minutes,Seconds,Dummy);
{ Calculate a new time stamp and update object fields: }
PutTimeStamp(CalcTimeStamp(Hours,Minutes,Seconds));
GetDate(Year,Month,Day,Dummy); { Get current clock date }
{ Calculate a new date stamp and update object fields: }
PutDateStamp(CalcDateStamp(Year,Month,Day));
END;
{---------------------------------------------------------------------}
{ This method allows us to apply a whole long integer time/date stamp }
{ such as that returned by the DOS unit's GetFTime procedure to the }
{ When object. The object divides the stamp into time and date }
{ portions and recalculates all other fields in the object. }
{---------------------------------------------------------------------}
PROCEDURE When.PutWhenStamp(NewWhen : LongInt);
BEGIN
WhenStamp := NewWhen;
{ We've actually updated the stamp proper, but we use the two }
{ "put" routines for time and date to generate the individual }
{ field and string representation forms of the time and date. }
{ I know that the "put" routines also update the long integer }
{ stamp, but while unnecessary it does no harm. }
PutTimeStamp(WhenUnion(WhenStamp).TimePart);
PutDateStamp(WhenUnion(WhenStamp).DatePart);
END;
{---------------------------------------------------------------------}
{ We can choose to update only the time stamp, and the object will }
{ recalculate only its time-related fields. }
{---------------------------------------------------------------------}
PROCEDURE When.PutTimeStamp(NewStamp : Word);
BEGIN
WhenUnion(WhenStamp).TimePart := NewStamp;
{ The time stamp is actually a bitfield, and all this shifting left }
{ and right is just extracting the individual fields from the stamp:}
Hours := NewStamp SHR 11;
Minutes := (NewStamp SHR 5) AND $003F;
Seconds := (NewStamp SHL 1) AND $001F;
{ Derive a string version of the time: }
CalcTimeString(TimeString,Hours,Minutes,Seconds);
END;
{---------------------------------------------------------------------}
{ Or, we can choose to update only the date stamp, and the object }
{ will then recalculate only its date-related fields. }
{---------------------------------------------------------------------}
PROCEDURE When.PutDateStamp(NewStamp : Word);
BEGIN
WhenUnion(WhenStamp).DatePart := NewStamp;
{ Again, the date stamp is a bit field and we shift the values out }
{ of it: }
Year := (NewStamp SHR 9) + 1980;
Month := (NewStamp SHR 5) AND $000F;
Day := NewStamp AND $001F;
{ Calculate the day of the week value using Zeller's Congruence: }
DayOfWeek := CalcDayOfWeek(Year,Month,Day);
{ Calculate the short string version of the date; as in "06/29/89": }
CalcDateString(DateString,Year,Month,Day);
{ Calculate a long version, as in "Thursday, June 29, 1989": }
CalcLongDateString(LongdateString,Year,Month,Day,DayOfWeek);
END;
PROCEDURE When.PutNewDate(NewYear,NewMonth,NewDay : Word);
BEGIN
{ The "boss" field is the date stamp. Everything else is figured }
{ from the stamp, so first generate a new date stamp, and then }
{ (odd as it may seem) regenerate everything else, *including* }
{ the Year, Month, and Day fields: }
PutDateStamp(CalcDateStamp(NewYear,NewMonth,NewDay));
{ Calculate the short string version of the date; as in "06/29/89": }
CalcDateString(DateString,Year,Month,Day);
{ Calculate a long version, as in "Thursday, June 29, 1989": }
CalcLongDateString(LongdateString,Year,Month,Day,DayOfWeek);
END;
PROCEDURE When.PutNewTime(NewHours,NewMinutes,NewSeconds : Word);
BEGIN
{ The "boss" field is the time stamp. Everything else is figured }
{ from the stamp, so first generate a new time stamp, and then }
{ (odd as it may seem) regenerate everything else, *including* }
{ the Hours, Minutes, and Seconds fields: }
PutTimeStamp(CalcTimeStamp(NewHours,NewMinutes,NewSeconds));
{ Derive the string version of the time: }
CalcTimeString(TimeString,Hours,Minutes,Seconds);
END;
END.
[LISTING TWO]
PROGRAM TimeTest;
USES Crt,TimeDate;
VAR
Now : When;
BEGIN
Write('At the tone, it will be exactly ');
Delay(1000);
Now.PutNow;
Sound(1000); Delay(100); NoSound;
WITH Now DO Writeln(TimeString,'m on ',LongDateString,'.');
Readln
END.